package com.ruoyi.pay.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.common.config.WxPayV2Config;
import com.ruoyi.common.constant.WxPayV2Url;
import com.ruoyi.common.core.domain.model.WxPayRequestBody;
import com.ruoyi.common.utils.WxPayCommonUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.pay.service.IWxPayV2Service;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

@Service
public class WxPayV2ServiceImpl implements IWxPayV2Service {

    private static final DecimalFormat df = new DecimalFormat("#");
    @Resource
    private WxPayV2Config wxPayConfig;

    /**
     * 微信支付统一预下单接口，请查看接口规则: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     *
     * @param payRequestBody
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> unifiedorder(WxPayRequestBody payRequestBody) throws Exception {
        //创建当前订单唯一的商户订单号。
        String trade_no = IdUtil.simpleUUID();
        Map<String, Object> result = new HashMap<String, Object>();
        //创建请求参数
        SortedMap<String, String> req = new TreeMap<String, String>();
        req.put("appid", wxPayConfig.getAppid());    //公众号/小程序id
        req.put("mch_id", wxPayConfig.getMch_id());  // 商户号
        req.put("nonce_str", WXPayUtil.generateNonceStr()); // 32位随机字符串
        req.put("body", payRequestBody.getDescription()); // 商品描述
        req.put("out_trade_no", trade_no);   // 商户订单号
        req.put("total_fee", df.format(Double.parseDouble(payRequestBody.getTotalFee()) * 100));    // total_fee表示标价金额，值类型为int，单位：分。示例：88表示0.88元。
        req.put("spbill_create_ip", IpUtils.getIpAddr());   // 终端IP
        req.put("notify_url", wxPayConfig.getNotify_url());  // 回调地址
        req.put("trade_type", payRequestBody.getTradeType());    // 交易类型
        req.put("attach", payRequestBody.getAttach());  // 附加数据
//        if (ObjectUtil.isNotEmpty(wxPayBean.getSub_mch_id())) {
//            //服务商模式
//            req.put("sub_mch_id", wxPayBean.getSub_mch_id());//子商户号    微信支付 分配的子商户号
//        }
//        if (ObjectUtil.isNotEmpty(wxPayBean.getTime_expire())) {
//            //设置订单结束时间
//            //交易结束时间 订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。
//            req.put("time_expire", wxPayBean.getTime_expire());
//        }
        if (ObjectUtil.isNotEmpty(payRequestBody.getOpenId())) {
            //JSAPI支付
            req.put("openid", payRequestBody.getOpenId());//用户标识 trade_type=JSAPI，此参数必传，用户在主商户appid下的唯一标识。openid和sub_openid可以选传其中之一，如果选择传sub_openid,则必须传sub_appid。下单前需要调用【网页授权获取用户信息: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 】接口获取到用户的Openid。
        }
        //签名。官方签名生成算法参考文档：https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
        req.put("sign", WXPayUtil.generateSignature(req, wxPayConfig.getApi_key(), WXPayConstants.SignType.MD5));

        //生成要发送的 xml
        String xmlBody = WXPayUtil.generateSignedXml(req, wxPayConfig.getApi_key());
        System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));

        //发送 POST 请求 统一下单 API 并携带 xmlBody 内容,然后获得返回接口结果
        String preOrderResult = WxPayCommonUtils.httpsRequest(WxPayV2Url.Uifiedorder, "POST", xmlBody);
        //输出结果示例：
        //<?xml version="1.0" encoding="UTF-8" standalone="no"?>
        //<xml>
        //<appid>wx15de247df91a38ae</appid>
        //<attach>附加数据test</attach>
        //<body>泡泡豚游艇-包船费</body>
        //<mch_id>1649918109</mch_id>
        //<nonce_str>cf24e0000956431fa7c06cded81def04</nonce_str>
        //<notify_url>https://www.baidu.com</notify_url>
        //<openid>owVPG5edQz9uaZqo7XxVP2cdHt7E</openid>
        //<out_trade_no>5b40b3addebc40aabc5c84ed9bfc4d4f</out_trade_no>
        //<sign>0D3577BEC9E2F90DEEEF46D2AC537C85</sign>
        //<spbill_create_ip>127.0.0.1</spbill_create_ip>
        //<total_fee>1</total_fee>
        //<trade_type>JSAPI</trade_type>
        //</xml>
        System.err.println(String.format("%s", preOrderResult));

        //将返回结果从 xml 格式转换为 map 格式
        Map<String, String> wxResultMap = WXPayUtil.xmlToMap(preOrderResult);

        //判断通信状态 此字段是通信标识，非交易标识
        if (ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && wxResultMap.get("return_code").equals("SUCCESS")) {
            //业务结果
            if (wxResultMap.get("result_code").equals("SUCCESS")) {
                //预下单成功
                result.put("code", 0);
                result.put("msg", "预下单成功");
                //微信订单号
                result.put("out_trade_no", trade_no);
                switch (wxResultMap.get("trade_type")) {
                    case "NATIVE":
                        //二维码地址
                        result.put("QrCode", wxResultMap.get("code_url"));
                        break;
                    case "MWEB":
                        //二维码地址
                        result.put("mweb_url", wxResultMap.get("mweb_url"));
                        break;
                    case "JSAPI":
                        //预支付交易会话标识 微信生成的预支付回话标识，用于后续接口调用中使用，该值有效期为2小时
                        String appId = wxPayConfig.getAppid();
                        String nonceStr = WXPayUtil.generateNonceStr();
                        String packageStr = "prepay_id=" + wxResultMap.get("prepay_id");
                        String timeStamp = String.valueOf(System.currentTimeMillis());

                        SortedMap<String, String> req1 = new TreeMap<String, String>();
                        req1.put("appId", appId);
                        req1.put("timeStamp", timeStamp);
                        req1.put("nonceStr", nonceStr);
                        req1.put("package", packageStr);
                        req1.put("signType", "MD5");


                        result.put("timeStamp", timeStamp);
                        result.put("nonceStr", nonceStr); // 32位随机字符串
                        result.put("package", packageStr);
                        result.put("signType", "MD5");
                        result.put("paySign", WXPayUtil.generateSignature(req1, wxPayConfig.getApi_key(), WXPayConstants.SignType.MD5));

                        break;
                }
            } else {
                //下单失败
                result.put("code", 2);
                result.put("msg", wxResultMap.get("err_code_des"));
            }
        } else {
            //通信异常
            result.put("code", 2);
            result.put("msg", wxResultMap.get("return_msg"));//当return_code为FAIL时返回信息为错误原因 ，例如 签名失败 参数格式校验错误
        }
        return result;
    }

    /**
     * 查询微信支付订单状态。
     *
     * @param orderNo
     * @return
     */
    @Override
    public Map<String, Object> queryPayStatus(String orderNo) throws Exception {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        //创建请求参数
        SortedMap<String, String> req = new TreeMap<String, String>();
        req.put("appid", wxPayConfig.getAppid()); // 公众号ID
        req.put("mch_id", wxPayConfig.getMch_id());   // 商户号
        req.put("out_trade_no", orderNo);//商户订单号
        req.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串
//        if (ObjectUtil.isNotEmpty(wxPayBean.getSub_mch_id())) {
//            req.put("sub_mch_id", wxPayBean.getSub_mch_id());//子商户号 微信支付 分配的子商户号（服务商模式的时候填入）
//        }
        req.put("sign", WXPayUtil.generateSignature(req, wxPayConfig.getApi_key(), WXPayConstants.SignType.MD5));
        //生成要发送的 xml
        String xmlBody = WXPayUtil.generateSignedXml(req, wxPayConfig.getApi_key());
        System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));
        //调用查询订单支付状态 API
        String result = WxPayCommonUtils.httpsRequest(WxPayV2Url.Orderquery, "POST", xmlBody);
        //返回解析后的 map 数据
        Map<String, String> wxResultMap = WXPayUtil.xmlToMap(result);
        resultMap.put("order_no", wxResultMap.get("out_trade_no"));
        //判断通信状态 此字段是通信标识，非交易标识
        if (ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && wxResultMap.get("return_code").equals("SUCCESS")) {
            //业务结果
            if (wxResultMap.get("result_code").equals("SUCCESS")) {
                //状态查询成功
//                resultMap.put("code", 0);
                switch (wxResultMap.get("trade_state")) {
                    case "SUCCESS":
//                        resultMap.put("OrderCode", wxResultMap.get("trade_state"));//订单交易状态code
//                        resultMap.put("msg", "支付成功");
//                        resultMap.put("out_trade_no", wxResultMap.get("out_trade_no"));//商户订单号
//                        resultMap.put("time_end", wxResultMap.get("time_end"));//支付完成时间
//                        resultMap.put("attach", wxResultMap.get("attach"));//下单时候传过去的附加数据

                        resultMap.put("code", 0);
                        resultMap.put("msg", "支付成功");
                        break;
                    case "NOTPAY":
                        resultMap.put("code", 1);
                        resultMap.put("msg", "未支付");
                        break;
                    case "USERPAYING":
                        resultMap.put("code", 2);
                        resultMap.put("msg", "用户支付中");
                        break;
                    case "REFUND":
                        resultMap.put("code", 3);
                        resultMap.put("msg", "转入退款");
                        break;
                    case "CLOSED":
                        resultMap.put("code", 4);
                        resultMap.put("msg", "已关闭");
                        break;
                    case "REVOKED":
                        resultMap.put("code", 5);
                        resultMap.put("msg", "已撤销(刷卡支付)");
                        break;
                    case "PAYERROR":
                        resultMap.put("code", 6);
                        resultMap.put("msg", "支付失败(其他原因，如银行返回失败)");
                        break;
                    case "ACCEPT":
                        resultMap.put("code", 7);
                        resultMap.put("msg", "已接收，等待扣款");
                        break;
                }
            } else {
                //下单失败
                resultMap.put("code", 2);
                resultMap.put("msg", wxResultMap.get("err_code_des"));
            }
        } else {
            //通信异常
            resultMap.put("code", 2);
            resultMap.put("msg", wxResultMap.get("return_msg"));//当return_code为FAIL时返回信息为错误原因 ，例如 签名失败 参数格式校验错误
        }
        System.out.println(String.format("%s", wxResultMap));
        return resultMap;
    }

    /**
     * 微信支付申请退款。
     *
     * @return
     */
//    public Map<String, Object> refund(WxRefundReqBody refundRequestBody) throws Exception {
//        //创建当前退款订单的唯一的商户退款单号。
//        String out_refund_no = IdUtil.simpleUUID();
//        Map<String, Object> resultMap = new HashMap<String, Object>();
//        //创建请求参数
//        SortedMap<String, String> req = new TreeMap<>();
//        req.put("appid", wxPayConfig.getAppid()); // 公众号ID
//        req.put("mch_id", wxPayConfig.getMch_id());   // 商户号
//        req.put("nonce_str", WXPayUtil.generateNonceStr());// 长度32位的随机字符串
////        if (ObjectUtil.isNotEmpty(wxPayBean.getSub_mch_id())) {
////            req.put("sub_mch_id", wxPayBean.getSub_mch_id());//子商户号 微信支付 分配的子商户号（服务商模式的时候填入）
////        }
//        req.put("sign", WXPayUtil.generateSignature(req, wxPayConfig.getApi_key(), WXPayConstants.SignType.MD5));
//        req.put("sign_type", "MD5"); //此参数可选，默认为MD5。
//        req.put("out_trade_no", refundRequestBody.getOrderNo());//商户订单号
//        req.put("out_refund_no", out_refund_no);//商户退款单号
//        req.put("total_fee", df.format(Double.parseDouble(refundRequestBody.getTotalFee()) * 100));//订单金额
//        req.put("refund_fee", df.format(Double.parseDouble(refundRequestBody.getRefundFee()) * 100));//退款金额
//        req.put("refund_desc", refundRequestBody.getRefundDesc());//退款原因
//        //生成要发送的 xml
//        String xmlBody = WXPayUtil.generateSignedXml(req, wxPayConfig.getApi_key());
//        System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));
//
//        //调用查询订单支付状态 API
//        String result = WxPayCommonUtils.httpsRequest(WxPayV2Url.Refund, "POST", xmlBody);
//        //返回解析后的 map 数据
//        Map<String, String> wxResultMap = WXPayUtil.xmlToMap(result);
//        resultMap.put("order_no", wxResultMap.get("out_trade_no"));
//        //判断通信状态 此字段是通信标识，非交易标识
//        if (ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && wxResultMap.get("return_code").equals("SUCCESS")) {
//            //业务结果
//            if (wxResultMap.get("result_code").equals("SUCCESS")) {
//                //状态查询成功
////                resultMap.put("code", 0);
//            } else {
//                //下单失败
//                resultMap.put("code", 2);
//                resultMap.put("msg", wxResultMap.get("err_code_des"));
//            }
//        } else {
//            //通信异常
//            resultMap.put("code", 2);
//            resultMap.put("msg", wxResultMap.get("return_msg"));//当return_code为FAIL时返回信息为错误原因 ，例如 签名失败 参数格式校验错误
//        }
//        System.out.println(String.format("%s", wxResultMap));
//
//        return resultMap;
//    }

    /**
     *  初始化微信支付链接
     *  privateKey  商户API私钥
     *  apiv3  API v3密钥
     *  mchid  商户id
     *  serialNumber 商户API证书的证书序列号
     * @return
     */
//    public HttpClient initWChant(){
//        PrivateKey merchantPrivateKey = PemUtil
//                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));
//        String apiv3 = WChantPay.getApiv3();
//        String mchid = WChantPay.getMchid();
//        String serialNumber = WChantPay.getSerialNumber();
//        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
//                new WechatPay2Credentials(mchid, new PrivateKeySigner(serialNumber, merchantPrivateKey)),
//                apiv3.getBytes(StandardCharsets.UTF_8));
//        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//                .withMerchant(WChantPay.getMchid(), serialNumber, merchantPrivateKey)
//                .withValidator(new WechatPay2Validator(verifier));
//        return builder.build();
//    }

}
